﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using System.IO;
using System;
using System.Linq;

[System.Serializable]
public class SpineConfig
{
    public List<SpineConfigData> spineDataList = new List<SpineConfigData>();
}

[System.Serializable]
public class SpineConfigData
{
    public string name;
    public string jsonPath;
    public string skinName = "default";
    public string animName;
    public List<string> slotHides = new List<string>();
    public Vector3 size = Vector3.one;
    public Vector3 position = Vector3.zero;
    public bool isSizeFixed = false;
    public int sortingOrder = 0;
    public float alpha;
}

public class SpineShowData
{
    public Spine.ExposedList<Spine.Slot> slots;
    public Spine.ExposedList<Spine.Animation> anims;
    public Spine.ExposedList<Spine.Skin> skins;
}

public class SpineData
{
    public SpineConfigData spineConfigData;
    public SpineShowData spineShowData;
}

public class SpineCreateControl : MonoSingleton<SpineCreateControl>
{
    const string spinesConfigKey = "spinesConfig";

    private Material materialPropertySource;
    SpineConfig spineConfig;

    SpineConfigData currentSpineConfigData = new SpineConfigData();
    SpineShowData currentSpineShowData = new SpineShowData();
    SkeletonAnimation currentSpineAnimation = new SkeletonAnimation();
    Dictionary<GameObject, SpineData> currentSpineDic = new Dictionary<GameObject, SpineData>();

    bool isSpineLoadFinished;
    Queue<SpineConfigData> spinesToLoad = new Queue<SpineConfigData>();

    public SkeletonAnimation CurrentSpineAnimation
    {
        get
        {
            return currentSpineAnimation;
        }
    }

    public SpineShowData CurrentSpineShowData
    {
        get
        {
            return currentSpineShowData;
        }
    }

    public SpineConfigData CurrentSpineConfigData
    {
        get
        {
            return currentSpineConfigData;
        }
    }

    protected override void Init()
    {
        base.Init();
        currentSpineShowData = new SpineShowData();
        materialPropertySource = Resources.Load<Material>("Material/mat");
        LoadSpineConfigs();

        EventBus.Instance.AddEventHandler(EventID.OnOneSpineLoaded, OnOneSpineLoaded);
    }

    public void LoadSpineConfigs()
    {
        string json = PlayerPrefs.GetString(spinesConfigKey);
        if (string.IsNullOrEmpty(json))
        {
            Debug.LogError("SpineConfigs is null");
            spineConfig = new SpineConfig();
            return;
        }
        spineConfig = JsonUtility.FromJson<SpineConfig>(json);
        if (spineConfig.spineDataList.Count > 0)
        {
            spineConfig.spineDataList.ForEach(x => spinesToLoad.Enqueue(x));
            isSpineLoadFinished = true;
        }
    }

    private void OnOneSpineLoaded()
    {
        //加载后设置参数
        SetSkin(currentSpineConfigData.skinName);
        SetAnim(currentSpineConfigData.animName);
        foreach (var slotHide in currentSpineConfigData.slotHides)
        {
            SetSlotVisible(slotHide, false);
        }
    }

    public void SaveSpineConfigs()
    {
        string json = JsonUtility.ToJson(spineConfig);
        PlayerPrefs.SetString(spinesConfigKey, json);
        print(json);
    }

    public void LoadTheSpine(SpineConfigData configData)
    {
        if (configData == null)
        {
            return;
        }

        currentSpineConfigData = configData;
        string jsonFilePath = configData.jsonPath;

        if (!string.IsNullOrEmpty(jsonFilePath))
        {
            currentSpineConfigData.name = Path.GetFileName(jsonFilePath);

            string folderName = Path.GetDirectoryName(jsonFilePath);
            string spinePrefix = Path.GetFileNameWithoutExtension(jsonFilePath);
            string spineBasePath = Path.Combine(folderName, spinePrefix);

            string texturePath = string.Format("{0}.png", spineBasePath);

            if (File.Exists(texturePath))
            {
                StartCoroutine(Res.LoadExternalTexture(texturePath, (tex) =>
                {
                    SkeletonDataAsset runtimeSDA = GetRuntimeSDA(tex, jsonFilePath, spinePrefix, spineBasePath);
                    SetSpineObjectPara(runtimeSDA);
                    SetSpineShowData();
                    AddRequireComponents();
                    RecordSpineInfos();

                    //通知加载完成
                    EventBus.Instance.BroadCastEvent(EventID.OnOneSpineLoaded);

                    isSpineLoadFinished = true;

                }));
            }
        }
    }

    private void SetSpineShowData()
    {
        currentSpineShowData = new SpineShowData();
        currentSpineShowData.anims = currentSpineAnimation.skeletonDataAsset.GetSkeletonData(true).Animations;
        currentSpineShowData.skins = currentSpineAnimation.skeleton.Data.Skins;
        currentSpineShowData.slots = currentSpineAnimation.skeleton.Slots;
    }

    private void SetSpineObjectPara(SkeletonDataAsset runtimeSDA)
    {
        //位置与缩放
        currentSpineAnimation.transform.SetParent(transform);
        currentSpineAnimation.transform.position = currentSpineConfigData.position;
        currentSpineAnimation.transform.localScale = currentSpineConfigData.size;
        currentSpineAnimation.transform.gameObject.name = runtimeSDA.name.ToString();
        currentSpineAnimation.GetComponent<MeshRenderer>().sortingOrder = currentSpineConfigData.sortingOrder;
    }

    private void AddRequireComponents()
    {
        //添加必要组件
        currentSpineAnimation.gameObject.AddComponent<BoxCollider>();
        currentSpineAnimation.gameObject.AddComponent<SpineDragger>();
        currentSpineAnimation.gameObject.AddComponent<SpineScaler>();
    }

    private void RecordSpineInfos()
    {
        if (!spineConfig.spineDataList.Contains(currentSpineConfigData))
            spineConfig.spineDataList.Add(currentSpineConfigData);

        //记录参数
        if (!currentSpineConfigData.isSizeFixed)
        {
            FixScale();
            currentSpineConfigData.isSizeFixed = true;
        }
        currentSpineConfigData.size = currentSpineAnimation.transform.localScale;
        currentSpineConfigData.position = currentSpineAnimation.transform.position;

        //记录当前加载的spine
        if (!currentSpineDic.ContainsKey(currentSpineAnimation.gameObject))
        {
            currentSpineDic.Add(currentSpineAnimation.gameObject, new SpineData() { spineShowData = currentSpineShowData, spineConfigData = currentSpineConfigData });
        }
    }

    private SkeletonDataAsset GetRuntimeSDA(Texture2D tex, string jsonFilePath, string spinePrefix, string spineBasePath)
    {
        var atlasText = new TextAsset(File.ReadAllText(spineBasePath + ".atlas"));

        tex.name = spinePrefix;
        Texture2D[] textures = new Texture2D[] { tex };
        var skeletonJson = new TextAsset(File.ReadAllText(jsonFilePath));
        var runtimeSAA = SpineAtlasAsset.CreateRuntimeInstance(atlasText, textures, materialPropertySource, true);
        var runtimeSDA = SkeletonDataAsset.CreateRuntimeInstance(skeletonJson, runtimeSAA, true);

        runtimeSDA.GetSkeletonData(false); //预加载
        runtimeSDA.name = skeletonJson.name + "_SkeletonData";
        currentSpineAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSDA);  //初始化游戏对象
        currentSpineAnimation.loop = true;
        return runtimeSDA;
    }

    public void UpdateCurrentSpineObject(GameObject spineObject)
    {
        if (currentSpineDic.ContainsKey(spineObject))
        {
            var spineData = currentSpineDic[spineObject];
            currentSpineConfigData = spineData.spineConfigData;
            currentSpineShowData = spineData.spineShowData;

            //更新相关参数
            currentSpineConfigData.position = spineObject.transform.position;
            currentSpineConfigData.size = spineObject.transform.localScale;

            currentSpineAnimation = spineObject.GetComponent<SkeletonAnimation>();
            EventBus.Instance.BroadCastEvent<GameObject>(EventID.OnOneSpineSelected, spineObject);
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
        {
            SaveSpineConfigs();
        }

        if (Input.GetKeyDown(KeyCode.D))
        {
            spineConfig.spineDataList.Remove(currentSpineConfigData);
            currentSpineDic.Remove(currentSpineAnimation.gameObject);
            if (spineConfig.spineDataList.Count > 0)
            {
                currentSpineConfigData = spineConfig.spineDataList[0];
            }
            Destroy(currentSpineAnimation.gameObject);
        }

        if (Input.GetKeyDown(KeyCode.X))
        {
            var trans = CurrentSpineAnimation.transform;
            trans.localScale = new Vector3(trans.localScale.x * -1, trans.localScale.y, trans.localScale.z);
            CurrentSpineConfigData.size = trans.localScale;
        }

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            MeshRenderer renderer = currentSpineAnimation.GetComponent<MeshRenderer>();
            renderer.sortingOrder += 1;
            currentSpineConfigData.sortingOrder = renderer.sortingOrder;
        }

        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            MeshRenderer renderer = currentSpineAnimation.GetComponent<MeshRenderer>();
            renderer.sortingOrder -= 1;
            currentSpineConfigData.sortingOrder = renderer.sortingOrder;
        }

        //一个一个加载
        if (isSpineLoadFinished)
        {
            isSpineLoadFinished = false;
            if (spinesToLoad.Count > 0)
            {
                var spineConfig = spinesToLoad.Dequeue();
                LoadTheSpine(spineConfig);
            }
        }
    }

    public void SetSkin(string skinName)
    {
        //print("SetSkin:" + skinName);
        currentSpineAnimation.skeleton.SetSkin(skinName);
        currentSpineAnimation.skeleton.SetSlotsToSetupPose();
        currentSpineAnimation.AnimationState.Apply(currentSpineAnimation.skeleton);
        currentSpineConfigData.skinName = skinName;

    }

    public void SetAnim(string animName)
    {
        if (string.IsNullOrEmpty(animName))
        {
            currentSpineConfigData.animName = currentSpineShowData.anims.Items[0].Name;
        }
        else
        {
            currentSpineConfigData.animName = animName;
        }
        currentSpineAnimation.AnimationName = currentSpineConfigData.animName;
        print("SetAnim:" + animName);
    }

    public void SetSlotVisible(string slotName, bool visible)
    {
        //print("SetSlotVisible:" + slotName + ":" + visible);
        //记录隐藏的slot
        if (!visible)
        {
            currentSpineAnimation.skeleton.FindSlot(slotName).A = visible ? 1 : 0;
            if (!CurrentSpineConfigData.slotHides.Contains(slotName))
            {
                CurrentSpineConfigData.slotHides.Add(slotName);
            }
        }
        else
        {
            if (CurrentSpineConfigData.slotHides.Contains(slotName))
            {
                CurrentSpineConfigData.slotHides.Remove(slotName);
            }
        }
    }

    void FixScale()
    {
        MeshRenderer renderer = currentSpineAnimation.GetComponent<MeshRenderer>();
        Vector3 rightTop = renderer.bounds.center + renderer.bounds.extents;
        Vector3 leftDown = renderer.bounds.center - renderer.bounds.extents;
        Vector3 vrt = Camera.main.WorldToViewportPoint(rightTop);
        Vector3 vld = Camera.main.WorldToViewportPoint(leftDown);
        float radio = 1f / Mathf.Abs(vrt.y - vld.y);
        currentSpineAnimation.transform.localScale = radio * Vector3.one;
        currentSpineAnimation.transform.position -= renderer.bounds.center - transform.position;
    }
}
